﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Practices.Unity;

using SharedGenomics.Core;
using SharedGenomics.Workbench.Messages;

namespace SharedGenomics.Workbench.Core
{
    public class ApplicationController : IApplicationController, IEventSubscriber<ShowPresenterMessage>, IEventSubscriber<OpenTaskMessage>
    {
        private IDictionary<IPresenter, Delegate> _presenterCloseActions;
        private ITask _currentTask;

        public ApplicationController(IViewFactory viewFactory, ITaskRegistry taskRegistry, PresenterFactory presenterFactory)
        {
            this.ViewFactory = viewFactory;

            this.Tasks = taskRegistry;
            this.PresenterFactory = presenterFactory;

            this._presenterCloseActions = new Dictionary<IPresenter, Delegate>();
        }

        public void OpenTask(ITask task)
        {
            task.ThrowIfNull("task");
            this.CurrentTask = task;
            this.ShowPresenter(task.CurrentPresenter, FormDisplayMode.InPlace);
        }

        public void OpenTask(string taskName)
        {
            this.OpenTask(this.Tasks[taskName]);
        }

        public void CreateAndShowPresenter<T>(FormDisplayMode displayMode) where T : IPresenter
        {
            this.CreateAndShowPresenter<T>(displayMode, null, null);
        }

        public void CreateAndShowPresenter<T>(FormDisplayMode displayMode, Action<T> initialisationAction) where T : IPresenter
        {
            this.CreateAndShowPresenter(displayMode, initialisationAction, null);
        }

        public void CreateAndShowPresenter<T>(FormDisplayMode displayMode, Action<T> initialisationAction, Action<T> closeAction) where T : IPresenter
        {
            IPresenter presenter = this.PresenterFactory.Get<T>(false, initialisationAction);

            if (closeAction != null)
            {
                presenter.RequestingClose += new EventHandler(Presenter_RequestingClose);
                this._presenterCloseActions[presenter] = closeAction;
            }

            this.ShowPresenter(presenter, displayMode);
        }

        #region IEventSubscriber<ShowPresenterMessage> Members

        public void Receive(ShowPresenterMessage message)
        {
            message.ThrowIfNull("message");
            PresenterInitialisationContext context = new PresenterInitialisationContext()
            {
                AddToCache = false,
                Current = message.Existing,
                InitialisationAction = message.InitialisationAction,
                PresenterType = message.PresenterType
            };

            //create presenter
            IPresenter presenter = this.PresenterFactory.Get(context);

            //add close action if one is specified
            var closeAction = message.CloseAction;
            if (closeAction != null)
            {
                presenter.RequestingClose += new EventHandler(Presenter_RequestingClose);
                this._presenterCloseActions[presenter] = closeAction;
            }

            this.ShowPresenter(presenter, message.DisplayMode);
        }

        #endregion

        #region IEventSubscriber<OpenTaskMessage> Members

        public void Receive(OpenTaskMessage message)
        {
            message.ThrowIfNull("message");
            this.OpenTask(message.Task);
        }

        #endregion

        private void Presenter_RequestingClose(object sender, EventArgs e)
        {
            IPresenter presenter = sender as IPresenter;
            if (presenter != null && this._presenterCloseActions.ContainsKey(presenter))
            {
                Delegate closeAction = this._presenterCloseActions[presenter];

                //de-register for close event so this handler does not stop the presenter from being garbage collected
                presenter.RequestingClose -= this.Presenter_RequestingClose;

                //invoke close action
                closeAction.DynamicInvoke(presenter);
            }
        }

        public void AddMenu(IMenuFactory menuFactory, CommandFactory commandFactory)
        {
            menuFactory.ThrowIfNull("menuFactory");

            IMenu menu = menuFactory.CreateMainMenu();

            foreach (ITask t in this.Tasks)
            {
                //get item view and associate it with a controller
                IMenuItem item = menuFactory.CreateMainMenuItem();
                item.DisplayText = t.DisplayName;
                item.Command = commandFactory.CreateOpenTaskCommand(t);

                menu.AddItem(item);
            }

            //add menu to main form
            this.MainFormPresenter.SetMainMenu(menu);
        }

        public IView MainView
        {
            get { return this.MainFormPresenter.View; }
        }

        public void ShowPresenter(IPresenter presenter, FormDisplayMode displayMode)
        {
            if (displayMode == FormDisplayMode.InPlace)
            {
                //create view for presenter
                IView view = this.ViewFactory.CreateView(presenter);
                this.MainFormPresenter.SetMainView(view);
            }
            else
            {
                IViewDialog dialog = this.ViewFactory.CreateDialog(presenter);
                if (displayMode == FormDisplayMode.ModalDialog)
                {
                    dialog.ShowModal();
                }
                else
                {
                    dialog.Show();
                }
            }
        }

        private void CurrentTaskPresenterChanged(object sender, EventArgs e)
        {
            this.ShowPresenter(this.CurrentTask.CurrentPresenter, FormDisplayMode.InPlace);
        }

        private IViewFactory ViewFactory { get; set; }
        private ITaskRegistry Tasks { get; set; }
        private PresenterFactory PresenterFactory { get; set; }
        private IMainFormPresenter MainFormPresenter
        {
            get { return (IMainFormPresenter)this.PresenterFactory.Get<IMainFormPresenter>(true); }
        }

        private ITask CurrentTask
        {
            get { return this._currentTask; }
            set
            {
                Debug.Assert(value != null);

                //de-register presenter changed handler for current task if one is set
                if (this._currentTask != null)
                    this._currentTask.CurrentPresenterChanged -= this.CurrentTaskPresenterChanged;

                this._currentTask = value;
                this._currentTask.CurrentPresenterChanged += this.CurrentTaskPresenterChanged;
            }
        }
    }
}
